﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Linq;
using System.Windows.Forms;
using Krile.Cores.KeyAssignElems;
using Krile.Kernel.WindowsEx;

namespace Krile.Forms.ConfigForm.Children
{
    public partial class KeyAssignment : Krile.Kernel.Control.ConfigPage
    {
        #region CLR Junction
#if !PURE_CLR
        Krile.Imported.Controls.HotkeyControl ShowMainWindowHotKey = new Krile.Imported.Controls.HotkeyControl();
        Krile.Imported.Controls.HotkeyControl ShowPostBoxHotKey = new Krile.Imported.Controls.HotkeyControl();
#endif
        public KeyAssignment()
        {
            InitializeComponent();
#if !PURE_CLR
            ShowMainWindowHotKey.Parent = ShowMainWindowHotkeyFrame;
            ShowMainWindowHotKey.Dock = DockStyle.Fill;
            ShowPostBoxHotKey.Parent = ShowPostBoxHotkeyFrame;
            ShowPostBoxHotKey.Dock = DockStyle.Fill;
#endif
        #endregion
        }

        List<string> categories = new List<string>();
        KeyAssignCallbackCandidate[] candidates = null;
        Dictionary<Keys, List<KeyAssignMethodData>> kadatas = new Dictionary<Keys, List<KeyAssignMethodData>>();
        Dictionary<Keys, bool> enableOnText = new Dictionary<Keys, bool>();

        Keys _target = Keys.None;
        Keys target
        {
            get { return _target; }
            set
            {
                if (value == Keys.None) return;
                assignEditPanel.Enabled = true;
                enableAssignOnTextBox.Enabled = true;
                tgKeyInput.Text = value.ToString();
                var k = GetFormattedKey(value);
                _target = k;
                if (!kadatas.ContainsKey(k))
                    kadatas.Add(k, new List<KeyAssignMethodData>());
                if (!enableOnText.ContainsKey(k))
                    enableOnText.Add(k, false);
                UpdateAssigns();
            }
        }

        private void KeyAssignment_Load(object sender, EventArgs e)
        {
            candidates = Core.KeyAssign.GetCandidatesArray();
            foreach (var i in candidates)
            {
                int idx;
                if (i.Description == null)
                {
                    MessageBoxEx.Show("Undescripted element has detected.",
                        i.CallbackMethod.Method.Name + Environment.NewLine +
                        "This element is ignored.", "Key assignment configuration error",
                         MessageBoxEx.MessageBoxExButtons.Close, MessageBoxEx.MessageBoxExIcon.Error);
                    continue;
                }
                if ((idx = i.Description.IndexOf(":")) > 0)
                {
                    //Category:Descript
                    var str = i.Description.Substring(0, idx);
                    if (!categories.Contains(str))
                        categories.Add(str);
                }
            }
            if (Core.Config.KeyAssign.KeyAssignData != null)
            {
                foreach (var kid in Core.KeyAssign.AssignDataArray)
                {
                    if (kid.InvokeMethodDatas != null)
                    {
                        kadatas.Add(kid.LinkedKeys, new List<KeyAssignMethodData>(kid.InvokeMethodDatas));
                        enableOnText.Add(kid.LinkedKeys, kid.EnableOnTextBox);
                    }
                }
            }
            categoryList.Items.Add("-");
            categoryList.Items.AddRange(categories.ToArray());
            categoryList.SelectedIndex = 0;
#if PURE_CLR
            ghGroup.Enabled = false;
#else
            ShowMainWindowHotKey.Hotkey = Core.Config.KeyAssign.ShowMainWindowHotKey;
            ShowPostBoxHotKey.Hotkey = Core.Config.KeyAssign.ShowPostBoxHotKey;
#endif
            UpdateCandidates();
        }

        public override void Apply()
        {
            List<KeyAssignData> kads = new List<KeyAssignData>();
            foreach (var i in kadatas.Keys)
            {
                if (kadatas[i].Count > 0)
                {
                    kads.Add(new KeyAssignData()
                    {
                        LinkedKeys = i,
                        InvokeMethodDatas = kadatas[i].ToArray(),
                        EnableOnTextBox = enableOnText.ContainsKey(i) ? enableOnText[i] : false
                    });
                }
            }
            Core.Config.KeyAssign.KeyAssignData = kads.ToArray();
#if !PURE_CLR
            Core.Config.KeyAssign.ShowMainWindowHotKey = ShowMainWindowHotKey.Hotkey;
            Core.Config.KeyAssign.ShowPostBoxHotKey = ShowPostBoxHotKey.Hotkey;
#endif
        }

        private void tgKeyInput_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                    e.IsInputKey = true;
                    break;
            }
        }

        private void tgKeyInput_KeyDown(object sender, KeyEventArgs e)
        {
            target = e.KeyCode;
        }

        private void modsCheckedChanged(object sender, EventArgs e)
        {
            target = target & Keys.KeyCode;
        }

        private Keys GetFormattedKey(Keys raw)
        {
            raw &= Keys.KeyCode;
            if (modAlt.Checked)
                raw |= Keys.Alt;
            if (modControl.Checked)
                raw |= Keys.Control;
            if (modShift.Checked)
                raw |= Keys.Shift;
            return raw;
        }

        private void UpdateCandidates()
        {
            candidateList.Items.Clear();
            if (categoryList.SelectedIndex == 0)
            {
                candidateList.Items.AddRange(candidates);
            }
            else
            {
                var cands = from c in candidates
                            where c.Description.StartsWith((string)(categoryList.SelectedItem) + ":")
                            select c;
                candidateList.Items.AddRange(cands.ToArray<KeyAssignCallbackCandidate>());
            }
        }

        private void UpdateAssigns()
        {
            assignmentList.SuspendLayout();
            assignmentList.Items.Clear();
            foreach (var i in kadatas[_target])
            {
                assignmentList.Items.Add(i.Description);
            }
            enableAssignOnTextBox.Checked = enableOnText[_target];
            assignmentList.ResumeLayout();
        }

        private void categoryList_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateCandidates();
        }

        private void addAssign_Click(object sender, EventArgs e)
        {
            if (candidateList.SelectedIndex < 0) return;
            kadatas[target].Add(new KeyAssignMethodData((KeyAssignCallbackCandidate)candidateList.SelectedItem));
            UpdateAssigns();
            assignmentList.SelectedIndex = assignmentList.Items.Count - 1;
        }

        private void removeAssign_Click(object sender, EventArgs e)
        {
            if (assignmentList.SelectedIndex < 0) return;
            var i = assignmentList.SelectedIndex;
            kadatas[target].RemoveAt(i);
            UpdateAssigns();
            if (i >= assignmentList.Items.Count)
                i = assignmentList.Items.Count - 1;
            assignmentList.SelectedIndex = i;
        }

        private void enableAssignOnTextBox_CheckedChanged(object sender, EventArgs e)
        {
            enableOnText[_target] = enableAssignOnTextBox.Checked;
        }

        private void assignUp_Click(object sender, EventArgs e)
        {
            if (assignmentList.SelectedIndex < 1) return;
            var i = assignmentList.SelectedIndex;
            var tmp = kadatas[target][i];
            kadatas[target][i] = kadatas[target][i - 1];
            kadatas[target][i - 1] = tmp;
            UpdateAssigns();
        }

        private void assignDown_Click(object sender, EventArgs e)
        {
            if (assignmentList.SelectedIndex >= assignmentList.Items.Count - 1) return;
            var i = assignmentList.SelectedIndex;
            var tmp = kadatas[target][i];
            kadatas[target][i] = kadatas[target][i + 1];
            kadatas[target][i + 1] = tmp;
            UpdateAssigns();
        }

        private void exCapable_Click(object sender, EventArgs e)
        {
            exCandMenu.Show(exCapable, new Point(0, exCapable.Height));

        }

        private void btnInEx_Click(object sender, EventArgs e)
        {
            impExpMenu.Show(btnInEx, new Point(0, btnInEx.Height));
        }

        private void menuTab_Click(object sender, EventArgs e)
        {
            target = Keys.Tab;
        }

        private void menuEnter_Click(object sender, EventArgs e)
        {
            target = Keys.Enter;
        }

        private void menuEscape_Click(object sender, EventArgs e)
        {
            target = Keys.Escape;
        }

        private void menuImport_Click(object sender, EventArgs e)
        {
            using (var ofd = new OpenFileDialog())
            {
                ofd.Filter = "Krile key-assign data|*.kkd";
                ofd.Title = "Open key-assign definition...";
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    var ld = Imported.Snippets.Files.LoadXML<KrileKeyAssignDataDefinition>(ofd.FileName, true);
                    if (ld != null)
                    {
                        foreach (var kid in ld.assigns)
                        {
                            kid.DefreezeAll();
                            if (kid.InvokeMethodDatas != null)
                            {
                                if (kadatas.ContainsKey(kid.LinkedKeys))
                                {
                                    switch (MessageBoxEx.Show(
                                            String.Format(Lang.Msg.Config_KeyAssignDuplicated_Context, GetFormattedKey(kid.LinkedKeys)),
                                            Lang.Msg.Config_KeyAssignDuplicated, Lang.Msg.Config_KeyAssignDuplicated_Caption,
                                             MessageBoxEx.MessageBoxExButtons.YesNoCancel, MessageBoxEx.MessageBoxExIcon.Warning))
                                    {
                                        case MessageBoxEx.DialogResultEx.Yes:
                                            continue;
                                        case MessageBoxEx.DialogResultEx.No:
                                            kadatas[kid.LinkedKeys].Clear();
                                            kadatas[kid.LinkedKeys].AddRange(kid.InvokeMethodDatas);
                                            enableOnText[kid.LinkedKeys] = kid.EnableOnTextBox;
                                            break;
                                        case MessageBoxEx.DialogResultEx.Cancel:
                                        default:
                                            return;
                                    }
                                }
                                else
                                {
                                    kadatas.Add(kid.LinkedKeys, new List<KeyAssignMethodData>(kid.InvokeMethodDatas));
                                    enableOnText.Add(kid.LinkedKeys, kid.EnableOnTextBox);
                                }
                            }
                        }
                    }
                }
            }
        }

        private void menuExport_Click(object sender, EventArgs e)
        {
            using (var sfd = new SaveFileDialog())
            {
                sfd.Filter = "Krile key-assign data|*.kkd";
                sfd.DefaultExt = ".kkd";
                sfd.Title = "Save key-assign definition...";
                if (sfd.ShowDialog() == DialogResult.OK)
                {
                    List<KeyAssignData> kads = new List<KeyAssignData>();
                    foreach (var i in kadatas.Keys)
                    {
                        if (kadatas[i].Count > 0)
                        {
                            kads.Add(new KeyAssignData()
                            {
                                LinkedKeys = i,
                                InvokeMethodDatas = kadatas[i].ToArray(),
                                EnableOnTextBox = enableOnText.ContainsKey(i) ? enableOnText[i] : false
                            });
                        }
                    }
                    var sd = new KrileKeyAssignDataDefinition() { assigns = kads.ToArray() };
                    Imported.Snippets.Files.SaveXML<KrileKeyAssignDataDefinition>(sfd.FileName, sd);
                }
            }
        }

        public class KrileKeyAssignDataDefinition
        {
            public KeyAssignData[] assigns;
        }

        private void btnCallInitialConfig_Click(object sender, EventArgs e)
        {
            if (MessageBoxEx.Show(Lang.Msg.Config_KeyAssignOverwrite_Context, Lang.Msg.Config_KeyAssignOverwrite, Lang.Msg.Config_KeyAssignOverwrite_Caption,
                 MessageBoxEx.MessageBoxExButtons.YesNo, MessageBoxEx.MessageBoxExIcon.Warning) == MessageBoxEx.DialogResultEx.Yes)
            {
                var kadBackup = Core.Config.KeyAssign.KeyAssignData;
                using (var ia = new Forms.Dialogs.Popup.InitialAssign())
                {
                    if (ia.ShowDialog() == DialogResult.OK)
                    {
                        if (Core.Config.KeyAssign.KeyAssignData != null)
                        {
                            kadatas.Clear();
                            enableOnText.Clear();
                            foreach (var kid in Core.KeyAssign.AssignDataArray)
                            {
                                if (kid.InvokeMethodDatas != null)
                                {
                                    kadatas.Add(kid.LinkedKeys, new List<KeyAssignMethodData>(kid.InvokeMethodDatas));
                                    enableOnText.Add(kid.LinkedKeys, kid.EnableOnTextBox);
                                }
                            }
                        }
                    }
                }
                Core.Config.KeyAssign.KeyAssignData = kadBackup;
            }
        }
    }
}
